package com.mini.common.config;

import com.mini.common.thread.ThreadPoolTaskExecutorDecorator;
import com.mini.common.thread.ThreadPoolTaskSchedulerDecorator;
import com.mini.mysql.service.BaseService;
import com.mini.util.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
@Configuration
public class AsyncConfig implements AsyncConfigurer {

    @Bean("commonTaskExecutor")
    @Override
    public Executor getAsyncExecutor() {
        return getThreadPoolTaskExecutor(6, 12, 1024, "common-async-", true);
    }

    @Bean("asyncDataTaskExecutor")
    public Executor asyncDataTaskExecutor() {
        return getThreadPoolTaskExecutor(1, 1, 2, "order-async-", true);
    }

    @Bean("asyncData1TaskExecutor")
    public Executor asyncData1TaskExecutor() {
        return getThreadPoolTaskExecutor(1, 1, 2, "order-async-all-", true);
    }


    @Bean("asyncDataAttachTaskExecutor")
    public Executor asyncDataAttachTaskExecutor() {
        return getThreadPoolTaskExecutor(1, 1, 2, "attach-async-", true);
    }

    @Bean("asyncDataTeacherTaskExecutor")
    public Executor asyncDataTeacherTaskExecutor() {
        return getThreadPoolTaskExecutor(1, 1, 2, "teacher-async-", true);
    }

    @Bean("asyncDataBaseInfoTaskExecutor")
    public Executor asyncDataBaseInfoTaskExecutor() {
        return getThreadPoolTaskExecutor(1, 1, 2, "base-info-async-", true);
    }

    @Bean("asyncDataClassTaskExecutor")
    public Executor asyncDataClassTaskExecutor() {
        return getThreadPoolTaskExecutor(1, 1, 2, "calss-async-", true);
    }

    @Bean("asyncQueryDelDataTaskExecutor")
    public Executor asyncQueryDelDataTaskExecutor() {
        return getThreadPoolTaskExecutor(1, 1, 2, "del-async-", true);
    }



    @Bean("asyncBigOrderDataTaskExecutor")
    public Executor asyncBigOrderDataTaskExecutor() {
        return getThreadPoolTaskExecutor(1, 1, 2, "big-order-", true);
    }

    @Bean("asyncStatisticDataNewTaskExecutor")
    public Executor asyncStatisticDataNewTaskExecutor() {
        return getThreadPoolTaskExecutor(1, 1, 2, "statistic-async-", true);
    }


    @Bean("asyncSecondOrderInfoDataTaskExecutor")
    public Executor asyncSecondOrderInfoDataTaskExecutor() {
        return getThreadPoolTaskExecutor(1, 1, 2, "second-order-", true);
    }

    private ThreadPoolTaskScheduler getThreadPoolTaskScheduler(int corePoolSize, String threadNamePrefix, boolean waitForTasksToCompleteOnShutdown) {
        ThreadPoolTaskScheduler executor = new ThreadPoolTaskSchedulerDecorator();
        executor.setPoolSize(corePoolSize);
        executor.setThreadNamePrefix(threadNamePrefix);
        //设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean，这样这些异步任务的销毁就会先于外部资源的销毁
        executor.setWaitForTasksToCompleteOnShutdown(waitForTasksToCompleteOnShutdown);
        //用来设置线程池中任务的等待时间，如果超过这个时候还没有销毁就强制销毁，以确保应用最后能够被关闭，而不是阻塞住
        executor.setRejectedExecutionHandler(new AbortPolicy());
        executor.setThreadGroup(new CommonThreadGroup("CommonThreadGroup"));
        return executor;
    }

    private ThreadPoolTaskExecutor getThreadPoolTaskExecutor(int corePoolSize, int maxPoolSize, Integer queueSize, String threadNamePrefix, boolean waitForTasksToCompleteOnShutdown) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutorDecorator();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setThreadNamePrefix(threadNamePrefix);
        executor.setQueueCapacity(queueSize);
        executor.setThreadGroup(new CommonThreadGroup("CommonThreadGroup"));
        //设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean，这样这些异步任务的销毁就会先于外部资源的销毁
        executor.setWaitForTasksToCompleteOnShutdown(waitForTasksToCompleteOnShutdown);
        //用来设置线程池中任务的等待时间，如果超过这个时候还没有销毁就强制销毁，以确保应用最后能够被关闭，而不是阻塞住
        executor.setRejectedExecutionHandler(new AbortPolicy());
        return executor;
    }


    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SpringAsyncExceptionHandler();
    }

    class SpringAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
        @Override
        public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
            log.error("Exception = handleUncaughtException method = {} params = {}", method, objects);
            log.error("异步线程报错 {} ", throwable.getMessage());
            log.error("", throwable.fillInStackTrace());
            log.error("", throwable);
            baseService.sendEmail("异步线程报错 : method:" + method + "objects :" + objects + "Exception：" + throwable.getMessage());
        }
    }

    private BaseService baseService;


    static class CommonThreadGroup extends ThreadGroup {
        public CommonThreadGroup(String name) {
            super(name);
        }

        public CommonThreadGroup(ThreadGroup parent, String name) {
            super(parent, name);
        }

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            log.error("异步线程报错 Thread = {}", t);
            log.error("", e);
            SpringContextHolder.getBean(BaseService.class).sendEmail("异步线程报错 : e" + e.getMessage());
        }
    }


    /**
     * A handler for rejected tasks that throws a
     * {@code RejectedExecutionException}.
     */
    public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */
        public AbortPolicy() {
        }

        /**
         * Always throws RejectedExecutionException.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always
         */
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            BaseService baseService = SpringContextHolder.getBean(BaseService.class);
//            baseService.sendEmail("定时任务线程池数量超过先定数量" + "Task " + r.toString() +
//                    " rejected from " +
//                    e.toString());
            log.info("任务正在运行，丢弃当前任务 {}", e.getThreadFactory().toString());
        }
    }
}
